{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![alt text](https://github.com/callysto/callysto-sample-notebooks/blob/master/notebooks/images/Callysto_Notebook-Banner_Top_06.06.18.jpg?raw=true)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: scipy in /opt/conda/lib/python3.6/site-packages (1.1.0)\r\n"
     ]
    }
   ],
   "source": [
    "## Import various packages that enable this notebook to work\n",
    "\n",
    "import sys\n",
    "!{sys.executable} -m pip install scipy\n",
    "\n",
    "import numpy\n",
    "import pylab\n",
    "from scipy import optimize"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Define the Benefit and Cost Functions\n",
    "\n",
    "def Benefit(x):\n",
    "    return x*(4-x)\n",
    "\n",
    "def Cost(x):\n",
    "    return x*x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Define the Payoff functions for both players\n",
    "\n",
    "def APayoff(a,b): return Benefit(a+b)-Cost(a)\n",
    "def BPayoff(a,b): return Benefit(a+b)-Cost(b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "An investment of b = 0.0 yields a payoff of 2.04\n",
      "An investment of b = 0.1 yields a payoff of 2.3\n",
      "An investment of b = 0.2 yields a payoff of 2.52\n",
      "An investment of b = 0.3 yields a payoff of 2.7\n",
      "An investment of b = 0.4 yields a payoff of 2.84\n",
      "An investment of b = 0.5 yields a payoff of 2.94\n",
      "An investment of b = 0.6 yields a payoff of 3.0\n",
      "An investment of b = 0.7 yields a payoff of 3.02\n",
      "An investment of b = 0.8 yields a payoff of 3.0\n",
      "An investment of b = 0.9 yields a payoff of 2.94\n",
      "An investment of b = 1.0 yields a payoff of 2.84\n"
     ]
    }
   ],
   "source": [
    "## Suppose A invests a=0.6.  How should B respond?\n",
    "## Let's try all the values from 0 to 1, in increments of 0.1\n",
    "\n",
    "for b in range(0,11):\n",
    "    print(\"An investment of b =\", b/10, \"yields a payoff of\", round(BPayoff(0.6,b/10),4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Given an input value for the variable a, this function determines the optimal value of b.\n",
    "## Thus, after A invests some amount a, this function calculates B's best response move b that maximizes his payoff,\n",
    "## i.e., minimizes the value of -BPayoff.  We do this to take advantage of Python's Optimize.Minimize function.\n",
    "\n",
    "def GetBestB(a):\n",
    "    def func(x): return -BPayoff(a,x)\n",
    "    BestB = optimize.minimize_scalar(func,bounds=(0,1), method='bounded')\n",
    "    b = BestB.x\n",
    "    return b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.6999999999999995"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "GetBestB(0.6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(3.15, 3.02)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "## Thus, if A selects a=0.6, B will respond with b=0.7, which is the move that maximizes B's payoff.\n",
    "## Below we see the payoffs for both players.\n",
    "\n",
    "APayoff(0.6,0.7),BPayoff(0.6,0.7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Now that we can caculate GetBestB(a) for all values of a, A wants to select the value of a that maximizes her payoff.\n",
    "## The function below determines this value of a.\n",
    "\n",
    "def GetBestA():\n",
    "    def f(x): return -APayoff(x,GetBestB(x))\n",
    "    BestA = optimize.minimize_scalar(f,bounds=(0,1), method='bounded')\n",
    "    a = BestA.x\n",
    "    return a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A's optimal strategy is a = 0.40000000000000124\n",
      "B's optimal strategy is b = 0.7999999999999986\n",
      "The payoff to A is 3.1999999999999984 and the payoff to B is 2.7200000000000015\n"
     ]
    }
   ],
   "source": [
    "## Now we have Python determine the optimal values of a and b.\n",
    "\n",
    "a = GetBestA()\n",
    "b = GetBestB(a)\n",
    "print(\"A's optimal strategy is a =\", a)\n",
    "print(\"B's optimal strategy is b =\", b)\n",
    "print(\"The payoff to A is\", APayoff(a,b), \"and the payoff to B is\", BPayoff(a,b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Let's generalize this to other Benefit and Cost functions.\n",
    "## We'll assume that the Benefit and Cost functions are quadratic, i.e, Benefit(x)=px^2+qx+r and Cost(x)=sx^2+tx+u\n",
    "## The function below is simply a cut+paste of what we showed above.\n",
    "\n",
    "def CalculatePayoffs(p,q,r,s,t,u):\n",
    "    \n",
    "    def Benefit(x): return p*x*x + q*x + r\n",
    "    def Cost(x): return s*x*x + t*x + u\n",
    "    \n",
    "    def APayoff(a,b): return Benefit(a+b)-Cost(a)\n",
    "    def BPayoff(a,b): return Benefit(a+b)-Cost(b)\n",
    "    \n",
    "    def GetBestB(a):\n",
    "        def func(x): return -BPayoff(a,x)\n",
    "        BestB = optimize.minimize_scalar(func,bounds=(0,1), method='bounded')\n",
    "        b = BestB.x\n",
    "        return b\n",
    "\n",
    "    def GetBestA():\n",
    "        def f(x): return -APayoff(x,GetBestB(x))\n",
    "        BestA = optimize.minimize_scalar(f,bounds=(0,1), method='bounded')\n",
    "        a = BestA.x\n",
    "        return a\n",
    "\n",
    "    a = GetBestA()\n",
    "    b = GetBestB(a)\n",
    "    print(\"A's optimal strategy is a =\", a)\n",
    "    print(\"B's optimal strategy is b =\", b)\n",
    "    print(\"The payoff to A is\", APayoff(a,b), \"and the payoff to B is\", BPayoff(a,b))\n",
    "    print()\n",
    "    return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A's optimal strategy is a = 0.39999999999999575\n",
      "B's optimal strategy is b = 0.8000000000000033\n",
      "The payoff to A is 3.200000000000002 and the payoff to B is 2.7199999999999935\n",
      "\n"
     ]
    }
   ],
   "source": [
    "## Let's double-check our answers for our original question, where Benefit(x)=x(4-x)=-x^2+4x+0 and Cost(x)=x^2.\n",
    "## Thus, we have p=-1, q=4, r=0, s=1, t=0, u=0 in this example.\n",
    "\n",
    "CalculatePayoffs(-1,4,0,1,0,0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "a58fc58e8f8b492ea7c8c06774fe69fc",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "interactive(children=(IntSlider(value=-1, description='p', max=10, min=-10), IntSlider(value=4, description='q…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<function __main__.CalculatePayoffs(p, q, r, s, t, u)>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "## Now use the SLIDERS below to adjust the values of p,q,r,s,t,u.  Instantly we see the optimal values and payoffs.\n",
    "## Do you notice anything interesting?  \n",
    "\n",
    "from __future__ import print_function\n",
    "from ipywidgets import interact, interactive, fixed, interact_manual\n",
    "import ipywidgets as widgets\n",
    "\n",
    "interact(CalculatePayoffs, \n",
    "         p=widgets.IntSlider(min=-10, max=10, step=1, value=-1),\n",
    "         q=widgets.IntSlider(min=-10, max=10, step=1, value=4),\n",
    "         r=widgets.IntSlider(min=-10, max=10, step=1, value=0),\n",
    "         s=widgets.IntSlider(min=-10, max=10, step=1, value=1),\n",
    "         t=widgets.IntSlider(min=-10, max=10, step=1, value=0),\n",
    "         u=widgets.IntSlider(min=-10, max=10, step=1, value=0))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Here are some questions to ponder.  \n",
    "\n",
    "#### When is A's optimal strategy equal to a=0?   And when is it equal to a=1?\n",
    "#### When is B's optimal strategy equal to b=0?  And when is it equal to b=1?\n",
    "#### When is A's payoff higher than B's payoff?  And how does this relate to the first and second derivatives of the Benefit and Cost functions?\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Additional References\n",
    "\n",
    "https://mast.queensu.ca/~math9-12/The%20parent%20game.html\n",
    "\n",
    "https://en.wikipedia.org/wiki/Stackelberg_competition\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![alt text](https://github.com/callysto/callysto-sample-notebooks/blob/master/notebooks/images/Callysto_Notebook-Banners_Bottom_06.06.18.jpg?raw=true)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
